/*
 *	Page Scroller - jQuery Plugin (minified)
 *	Simple plugin to create a smooth scrolling page with updating links
 *  download by http://www.codefans.net
 *	Support at: http://dairien.com
 *
 *	Copyright (c) 2012 Dairien Boyd. All Rights Reserved
 *
 *	Version: BETA - IE Still Unsupported (2/2/2012)
 *	Requires: jQuery v1.3+
 *
 *	This library is released under the BSD license:
 *
 *	Redistribution and use in source and binary forms, with or without
 *	modification, are permitted provided that the following conditions are met:
 *
 *	Redistributions of source code must retain the above copyright notice, this
 *	list of conditions and the following disclaimer. Redistributions in binary
 *	form must reproduce the above copyright notice, this list of conditions and
 *	the following disclaimer in the documentation and/or other materials
 *	provided with the distribution. Neither the name Dairirien Boyd nor
 *	the names of its contributors may be used to endorse or promote products
 *	derived from this software without specific prior written permission.
 *	ÀÁÈËÖ®¼Ò  lanrenzhijia.com
 *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *	ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 *	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 *	DAMAGE.
 */
(function ($) {
    $.fn.extend({
        /**
         * 在当前元素中添加导航栏，并当前元素中的内容块与导航关联
         * @param options {object}
         *  - initIndex {number} 初始化时默认滚入视图的内容索引，默认为：0。设为 -1 则不滚动内容。
         *  - navTxtArr {array} 导航中显示的 string 数组。
         *  - sectionClass {string} 内容部分每块对应的 class，默认为："section"。
         *  - navClass {string} 导航中每块对应的 class，默认为："scrollNav"。
         *  - navBox {string} 当前元素中，导航栏的容器对应的 selector，不定义则认为是 $(this)
         *  - sectionBox {string} 当前元素中，决定内容块可见区域的容器对应的 selector，不定义则认为是 $(this)
         *  - scrollOffset {number} 每个内容块滚动进入视图的偏移（单位 px），默认为：20，即当某内容块上方 20px 到达 dom 顶部就认为它是当前项。
         *  - animationSpeed {number} 点击导航触发内容滚动的动画时长（单位 ms），默认为：500。
         *  - navRender {function(i,txt)} 导航中每项的渲染函数。
         *    - i {int} 当前渲染项对应的内容块索引
         *    - txt {string} 当前渲染项根据配置得到的显示文本（若定义了 navTxtArr，则为 navTxtArr[i]）
         *  - navIsHor {bool} 是否是水平方向的导航条，默认为 false。
         *  - animationBefore {function(i)} 导航引起滚动前执行的回调。
         *    - i {int} 当前渲染项对应的内容块索引
         *  - animationComplete {function(i)} 导航引起的滚动执行后执行的回调。
         *    - i {int} 当前渲染项对应的内容块索引
         *  - onChange {function(i)} 导航栏状态发生变化时执行的回调。
         *    - i {int} 当前渲染项对应的内容块索引
         * @returns {object} 初始化后生成的对象。
         *  - opts {object} 组件的配置属性
         *  - getCurInedx() 当前项索引，-1 为在第一块内容之前，senction.length 为在最后一块内容之后
         *  - next() {function} 滚动至下一个内容块
         *  - prev() {function} 滚动至上一个内容块
         *  - goto(i) {function} 滚动至索引为 i 的内容块
         *
         *  <el navBox class="pageScrollPrev pageScrollNext"> 上方有导航项:pageScrollPrev; 下方有导航项:pageScrollNext
         *    自动生成的导航结构及样式说明
         *    <div class="pageScroll-prev"></div>
         *    <ul class="pageScroll">
         *      <li class="scrollNav scrollNav_1 data-pscroll= active" 0"="">导航1</li>
         *      <li class="scrollNav scrollNav_2 data-pscroll=" 1"="">导航2</li>
         *      ...
         *    </ul>
         *    <div class="pageScroll-next"></div>
         *  </el>
         */
        pageScroller: function (options) {
            var dom=$(this);
            dom.addClass('pageScroller');
            var opts = $.extend({
                initIndex: 0
                ,navTxtArr: []
                ,sectionClass: "section"
                ,navClass: "scrollNav"
                ,sectionBox: ''
                ,navBox: ''
                ,navIsHor: false
                ,scrollOffset: 20
                ,animationSpeed: 500
                ,navRender: function(){}
                ,animationBefore: function () {}
                ,animationComplete: function () {}
                ,onChange: function () {}
            }, options);
            var sections=$("." + opts.sectionClass, dom)
                ,navsUl
                ,navs
                ,sectionBox = opts.sectionBox ? dom.find(opts.sectionBox) : []
                ,navBox = opts.navBox ? dom.find(opts.navBox) : []
                ,curIndex;
            sectionBox.length<=0 && (sectionBox=dom);
            navBox.length<=0 && (navBox=dom);

            /**
             * 滚动监听，根据 section 的状态修改导航栏状态
             */
            var _scrollHandler = function () {
                if (sectionBox.is(":animated")) return;// 正在执行动画效果
                var offset = sectionBox.find(sections.eq(0).offsetParent()).length ? // 滚动容器中找到内容块的定位元素，则两者不为同一元素
                    -sectionBox.scrollTop(): 0;
                for(var i= 0,cur,curTop,nextTop;i<sections.length;i++){
                    cur = sections.eq(i);
                    curTop = nextTop || cur.position().top-opts.scrollOffset;// 当前内容项距离顶部的位置
                    if(i==0 && curTop + offset > 0){// 未移入第一项
                        i=-1;
                        break;
                    }
                    if(i==sections.length-1){// 已经遍历到最后一项
                        nextTop = curTop + cur.height();// 当前内容项距离顶部的位置 + 高度
                    }else{
                        nextTop = sections.eq(i+1).position().top-opts.scrollOffset;// 下个内容项距离顶部的位置
                    }
                    if(curTop+offset <= 0 && nextTop+offset > 0){
                        break;
                    }
                }
                _updateLink(i);// 更新导航，可能为 -1 到 sections.length 的整数
            };
            /**
             * 将指定的 section 移入视图
             * @param i {number} section 对应的索引
             */
            var _updateSection = function(i) {
                if(i<0 || i>=sections.length || curIndex==i) return;// 超出范围或未发生变化
                var section=sections.eq(i)
                    ,top = section.position().top - opts.scrollOffset
                    ,offset = sectionBox.find(sections.eq(0).offsetParent()).length ? // 滚动容器中找到内容块的定位元素，则两者不为同一元素
                        0: sectionBox.scrollTop();
                if(top + offset != 0 && !sectionBox.is(":animated")){
                    sectionBox.animate({scrollTop: top + offset}, opts.animationSpeed, function () {
                        _updateLink(i);
                        opts.animationComplete(i);
                    });
                }
            };
            /**
             * 修改导航栏状态
             * @param i {number} 当前选中的 pageLink 对应的索引
             */
            var _updateLink = function (i) {
                // 最大值为 sections.length（调用 prev() 可跳转到最后一项），最小为 -1（调用 next() 可跳转到第一项）
                i = Math.min(sections.length,Math.max(-1,i));
                if(curIndex == i) return;// 未发生变化
                curIndex = i;
                navs.removeClass("active");
                if(i>=0 && i<sections.length){
                    navs.eq(i).addClass("active");
                    // 判断需要滚动以使当前项进入视图
                    _chargeNavScroll(i,'_updateLink');
                }
                opts.onChange();
            };
            // 判断是否需要滚动以使指定导航进入视图
            var _chargeNavScroll=function(i,fromFn){
                var p=opts.navIsHor?
                    ['left','right','scrollLeft']:// 水平方向
                    ['top','bottom','scrollTop'];
                var navBCR=navs.eq(i)[0].getBoundingClientRect()
                    ,ulBCR=navsUl[0].getBoundingClientRect()
                    ,oldTop=navsUl[p[2]]()
                    ,diff=0;
                if(ulBCR[p[0]]>navBCR[p[0]]){// 当前项在上方
                    diff=ulBCR[p[0]]-navBCR[p[0]];
                    navsUl[p[2]](oldTop-diff);
                }else if(ulBCR[p[1]]<navBCR[p[1]]){// 当前项在下方
                    diff=ulBCR[p[1]]-navBCR[p[1]];
                    navsUl[p[2]](oldTop-diff);
                }
                if(fromFn=='_updateLink') _scrollNavHandler(i);
            };
            /**
             * 导航部分滚动监听
             */
            var _scrollNavHandler=function(i){
                var newTop=navsUl.scrollTop();
                // 上方有内容，需显示向上箭头
                if(newTop>0){
                    navBox.addClass('pageScrollPrev');
                }else{
                    navBox.removeClass('pageScrollPrev');
                }
                // 下方有内容，需显示向下箭头
                if(newTop+navsUl.height()<navsUl[0].scrollHeight){
                    navBox.addClass('pageScrollNext');
                }else{
                    navBox.removeClass('pageScrollNext');
                }
                if(typeof i=='number'){
                    _chargeNavScroll(i);
                }
            };

            /** 主方法体 **/
            sectionBox.css('position')=='static' && sectionBox.css('position','relative');// 内容块中可能不存在定位元素
            /** 自动生成导航栏 **/
            var navisStr='<ul class="pageScroll">';
            sections.each(function (i) {
                $(this).attr('data-pscroll',i);
                var txt = opts.navTxtArr.length ? opts.navTxtArr[i] : ('导航' + (i+1));// 该内容块导航文本
                navisStr+= '<li class="' + opts.navClass + '" data-pscroll="'+i+'">';
                navisStr+= opts.navRender(i,txt) || txt;
                navisStr+= '</li>';
            });
            navisStr+='</ul>';
            navsUl=$(navisStr);
            navBox.append('<div class="pageScroll-prev"></div>')
                .append(navsUl)
                .append('<div class="pageScroll-next"></div>');
            navs = navsUl.children();
            /* 导航点击事件 */
            navBox.off('click.psNav')
                .on('click.psNav','.pageScroll li',function(ev){
                    if (sectionBox.is(":animated")) return;// 正在执行动画效果
                    // 导航栏点击事件
                    var i=navs.index($(this));
                    ev.preventDefault();
                    opts.animationBefore(i);
                    _updateSection(i);
                });
            sectionBox.off('scroll.ps')
                .on('scroll.ps',_scrollHandler);
            navsUl.off('scroll.ps')
                .on('scroll.ps',_scrollNavHandler);
            navBox.off('click.ps')
                .on('click.ps','.pageScroll-prev,.pageScroll-next',function(){
                    var oldTop=navsUl.scrollTop();
                    navsUl.scrollTop($(this).hasClass('pageScroll-prev') ?
                        oldTop-100 : oldTop+100);
                });
            $(window).off('resize.ps').on('resize.ps',_scrollNavHandler);
            // 初始化根据 opts.initIndex 滚动内容
            setTimeout(function () {
                _updateSection(opts.initIndex);
            }, 200);
            /** ================ 返回接口 ================ **/
            return {
                options:opts
                ,next: function () {
                    _updateSection(curIndex + 1);
                }
                ,prev: function () {
                    _updateSection(curIndex - 1);
                }
                /**
                 * 切换到指定内容块
                 * @param i {number} 内容块索引(从 0 开始)
                 */
                ,goto: function (i) {
                    _updateSection(i);
                }
            };
        }
    });
})(jQuery);